import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_frontend/environment_config.dart';

import 'package:flutter_frontend/utils/app_manager.dart';
import 'package:network_logger/network_logger.dart';
import 'package:path/path.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
import 'package:shared_preferences/shared_preferences.dart';

enum HttpMethod { get, post, delete, put }

class HttpManager {
  static final HttpManager _instance = HttpManager._internal();

  factory HttpManager() => _instance;

  static HttpManager get sharedInstance => _instance;

  late Dio _dio;

  /// 域名
  String? _hostUrl;

  Future<String> get hostUrl async {
    _hostUrl ??= await getEnvironment();
    return _hostUrl!;
  }

  HttpManager._internal() {
    _dio = Dio(BaseOptions(
        connectTimeout: const Duration(seconds: 60), sendTimeout: const Duration(seconds: 60), receiveTimeout: const Duration(seconds: 60), contentType: Headers.jsonContentType));
    if (kDebugMode) {
      _dio.interceptors.addAll([PrettyDioLogger(requestHeader: true, requestBody: true), DioNetworkLogger()]);
    } else {
      if (!EnvironmentConfig.isRelease) {
        _dio.interceptors.addAll([DioNetworkLogger()]);
      }
    }
  }

  Future<String> getEnvironment() async {
    if (EnvironmentConfig.isRelease) {
      return EnvironmentConfig.baseUrl;
    }
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    final String? environment = prefs.getString('ENVIRONMENT');
    if (environment == 'DEVELOP') {
      return EnvironmentConfig.baseUrlDevelop;
    } else if (environment == 'PROD') {
      return EnvironmentConfig.baseUrlProd;
    } else {
      return EnvironmentConfig.baseUrlTest;
    }
  }

  Future<void> saveEnvironment(String environment) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString('ENVIRONMENT', environment);
  }

  /// 网络请求
  Future<ApiCallBackInfo> requestData(String url, {HttpMethod? httpMethod, Map<String, dynamic>? queryParameters, dynamic data}) async {
    try {
      String? method;
      if (httpMethod == HttpMethod.post) {
        method = 'POST';
      } else if (httpMethod == HttpMethod.put) {
        method = 'PUT';
      } else if (httpMethod == HttpMethod.delete) {
        method = 'DELETE';
      } else {
        method = 'GET';
      }
      final Map<String, dynamic> headers = {'stamp': DateTime.now().millisecondsSinceEpoch, 'Content-Type': 'application/json'};
      final String? uuid = await AppManager.sharedInstance.uuid;
      if (uuid != null) headers['uuid'] = uuid;
      final String? token = await AppManager.sharedInstance.token;
      if (token != null) headers['Authorization'] = 'JWT ' + token;
      final Map<String, dynamic> queryParam = queryParameters ?? {};
      _hostUrl ??= await getEnvironment();
      String dataUrl = _hostUrl! + url;
      final Response response = await _dio.request(dataUrl, data: data, queryParameters: queryParam, options: Options(method: method, headers: headers));
      if (response.statusCode == 200) {
        final data = response.data;
        final Map<String, dynamic> json = data is String ? jsonDecode(data) : data;
        final ApiCallBackInfo apiCallBackInfo = ApiCallBackInfo()
          ..code = json['code']
          ..message = json['message']
          ..data = json['data'];
        if (apiCallBackInfo.code == 0) return apiCallBackInfo;
        throw DioError(response: response, type: DioErrorType.badResponse, requestOptions: RequestOptions(path: url));
      }
      return ApiCallBackInfo()
        ..code = response.statusCode ?? -1
        ..message = response.statusMessage ?? '未知错误';
    } catch (error) {
      final Map<String, dynamic> exceptionInfo = await handleException(error);
      return ApiCallBackInfo()
        ..code = exceptionInfo['code'] ?? -1
        ..message = exceptionInfo['message'] ?? '未知错误';
    }
  }

  /// 下载文件
  Future<String?> download(String url, String savePath, {Map<String, dynamic>? queryParameters, CancelToken? cancelToken, dynamic data, ProgressCallback? onReceiveProgress}) async {
    try {
      await Dio().download(url, savePath, queryParameters: queryParameters, onReceiveProgress: onReceiveProgress);
      return null;
    } on DioError catch (e) {
      String? message;
      if (CancelToken.isCancel(e)) {
        message = '下载已取消';
        EasyLoading.showInfo('下载已取消！');
      } else {
        if (e.response != null) {
          // _handleErrorResponse(e.response);
        } else {
          message = e.message;
        }
      }
      return message ?? '未知错误';
    } on Exception catch (error) {
      final Map<String, dynamic> exceptionInfo = await handleException(error);
      return exceptionInfo['message'] ?? '未知错误';
    }
  }

  /// 上传单个文件
  Future<String> uploadFile(String filesPath, {Map<String, dynamic>? dataMap}) async {
    final Map<String, dynamic> map = {'file': await MultipartFile.fromFile(filesPath)};
    map.addAll(dataMap ?? {});
    final ApiCallBackInfo info = await requestData('/api/application_service/web/files/upload/', httpMethod: HttpMethod.post, data: FormData.fromMap(map));
    return info.data['file_url'];
  }

  /// 上传多个文件
  // Future<List<String>> uploadFiles(List<String> filesPaths, {Map<String, dynamic>? dataMap}) async {
  //   final Map<String, dynamic> map = {'files': filesPaths.map((e) => MultipartFile.fromFileSync(e)).toList()};
  //   map.addAll(dataMap ?? {});
  //   final ApiCallBackInfo info =
  //       await requestData('/api/application_service/web/files/upload/', httpMethod: HttpMethod.post, data: FormData.fromMap(map));
  //   return [];
  // }

  /// 上传文件到阿里云
  Future<String> uploadFileToAliYun(
    String filePath, {
    required String url,
    required String dir,
    required String accessKeyId,
    required String policy,
    required String signature,
  }) async {
    final fileName = dir + '/' + basename(filePath);
    final Map<String, dynamic> map = {
      'key': fileName,
      'success_action_status': 200,
      'OSSAccessKeyId': accessKeyId,
      'policy': policy,
      'Signature': signature,
      'file': await MultipartFile.fromFile(filePath),
    };
    final Response response = await Dio().post(url, data: FormData.fromMap(map));
    if (response.statusCode == 200) {
      return url + '/' + fileName;
    }
    return '';
  }

  /// 错误捕捉
  Future<Map<String, dynamic>> handleException(dynamic error) async {
    final Map<String, dynamic> errorMap = {'code': -1, 'message': '未知错误'};
    if (error is DioError) {
      switch (error.type) {
        case DioErrorType.badResponse:
          final int? code = error.response?.statusCode;
          final String? message = error.response?.data['message'];
          if (code == 200) {
            final int? status = error.response?.data['code'];
            if (status == 401) {
              AppManager.sharedInstance.pushToLogin();
            } else if (status == 1) {
              AppManager.sharedInstance.systemMaintenance(message);
            }
            errorMap['code'] = status;
            errorMap['message'] = message;
          } else {
            errorMap['code'] = code;
            errorMap['message'] = message;
            if (code == 401) {
              AppManager.sharedInstance.pushToLogin();
            }
          }
          break;
        case DioErrorType.connectionTimeout:
          errorMap['message'] = '网络连接超时';
          errorMap['code'] = 405;
          break;
        case DioErrorType.sendTimeout:
          errorMap['message'] = '网络数据发送超时';
          errorMap['code'] = 406;
          break;
        case DioErrorType.receiveTimeout:
          errorMap['message'] = '网络数据接收超时';
          errorMap['code'] = 407;
          break;
        case DioErrorType.connectionError:
          errorMap['message'] = '连接失败';
          errorMap['code'] = 408;
          break;
        case DioErrorType.cancel:
          errorMap['message'] = '网络数据请求取消';
          errorMap['code'] = 409;
          break;
        case DioErrorType.badCertificate:
          errorMap['message'] = '证书损坏';
          errorMap['code'] = -1;
          break;
        case DioErrorType.unknown:
          errorMap['message'] = '未知错误';
          errorMap['code'] = -1;
          break;
      }
    } else if (error is JsonUnsupportedObjectError) {
      errorMap['message'] = 'JSON数据解析失败';
      errorMap['code'] = -1;
    } else if (error is TypeError) {
      errorMap['message'] = '数据解析失败';
      errorMap['code'] = -1;
    }
    return errorMap;
  }
}

/// 请求信息
class ApiCallBackInfo {
  late int code;
  late String message;
  dynamic data;

  get isSuccess => code == 0;

  get apiError => isSuccess ? null : Exception(message);
}
